/**
 * Copyright (c) iwindplus Technologies Co., Ltd.2024-2030, All rights reserved.
 */

package com.iwindplus.boot.web.manager.http;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.text.CharSequenceUtil;
import com.iwindplus.boot.domain.constant.CommonConstant.FileConstant;
import com.iwindplus.boot.util.FilesUtil;
import com.iwindplus.boot.web.manager.HttpClientHandler;
import jakarta.annotation.Resource;
import java.net.URI;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.util.UriComponentsBuilder;

/**
 * RestTemplate 工具管理器.
 *
 * @author zengdegui
 * @since 2025/02/15 21:45
 */
@Slf4j
@Getter
public class RestTemplateManager implements HttpClientHandler {

    @Resource
    private RestTemplate httpClient;

    /**
     * get同步请求（参数拼接）.
     *
     * @param url    路径（必填）
     * @param header 请求头（可选）
     * @param param  参数（可选）
     * @return String
     */
    @Override
    public String httpGet(String url, Map<String, String> header, Map<String, Object> param) {
        header = this.buildDefaultContentType(header, MediaType.APPLICATION_FORM_URLENCODED_VALUE);
        final HttpHeaders headers = this.buildHeader(header);
        final HttpEntity<String> requestEntity = new HttpEntity<>(headers);
        URI uri = this.getUri(url, this.buildMultiValueMap(param));
        return this.httpClient.exchange(uri, HttpMethod.GET, requestEntity, String.class).getBody();
    }

    /**
     * post同步请求（json）.
     *
     * @param url    路径（必填）
     * @param header 请求头（可选）
     * @param json   参数（必填）
     * @return String
     */
    @Override
    public String httpPostByJson(String url, Map<String, String> header, String json) {
        header = this.buildDefaultContentType(header, MediaType.APPLICATION_JSON_VALUE);
        final HttpHeaders headers = this.buildHeader(header);
        final HttpEntity<String> requestEntity = new HttpEntity<>(json, headers);
        return this.httpClient.exchange(url, HttpMethod.POST, requestEntity, String.class).getBody();
    }

    /**
     * post同步请求（FormBody表单）.
     *
     * @param url    路径（必填）
     * @param header 请求头（可选）
     * @param param  参数（可选）
     * @return String
     */
    @Override
    public String httpPostByForm(String url, Map<String, String> header, Map<String, Object> param) {
        header = this.buildDefaultContentType(header, MediaType.APPLICATION_FORM_URLENCODED_VALUE);
        final HttpHeaders headers = this.buildHeader(header);
        MultiValueMap<String, Object> body = this.buildFormBody(param);
        HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);
        return this.httpClient.exchange(url, HttpMethod.POST, requestEntity, String.class).getBody();
    }

    /**
     * post同步请求（MultipartBody表单）.
     *
     * @param url    路径（必填）
     * @param header 请求头（可选）
     * @param param  参数（可选）
     * @param files  文件集合（可选）
     * @return String
     */
    @Override
    public String httpPostByMultipart(String url, Map<String, String> header, Map<String, Object> param, List<MultipartFile> files) {
        header = this.buildDefaultContentType(header, MediaType.MULTIPART_FORM_DATA_VALUE);
        final HttpHeaders headers = this.buildHeader(header);
        MultiValueMap<String, Object> body = this.buildMultipartBody(param, files);
        HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);
        return this.httpClient.exchange(url, HttpMethod.POST, requestEntity, String.class).getBody();
    }

    /**
     * put同步请求（json）.
     *
     * @param url    路径（必填）
     * @param header 请求头（可选）
     * @param json   参数（必填）
     * @return String
     */
    @Override
    public String httpPutByJson(String url, Map<String, String> header, String json) {
        header = this.buildDefaultContentType(header, MediaType.APPLICATION_JSON_VALUE);
        final HttpHeaders headers = this.buildHeader(header);
        final HttpEntity<String> requestEntity = new HttpEntity<>(json, headers);
        return this.httpClient.exchange(url, HttpMethod.PUT, requestEntity, String.class).getBody();
    }

    /**
     * put同步请求（FormBody表单）.
     *
     * @param url    路径（必填）
     * @param header 请求头（可选）
     * @param param  参数（可选）
     * @return String
     */
    @Override
    public String httpPutByForm(String url, Map<String, String> header, Map<String, Object> param) {
        header = this.buildDefaultContentType(header, MediaType.APPLICATION_FORM_URLENCODED_VALUE);
        final HttpHeaders headers = this.buildHeader(header);
        MultiValueMap<String, Object> body = this.buildFormBody(param);
        HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);
        return this.httpClient.exchange(url, HttpMethod.PUT, requestEntity, String.class, param).getBody();
    }

    /**
     * put同步请求（MultipartBody表单）.
     *
     * @param url    路径（必填）
     * @param header 请求头（可选）
     * @param param  参数（可选）
     * @param files  文件集合（可选）
     * @return String
     */
    @Override
    public String httpPutByMultipart(String url, Map<String, String> header, Map<String, Object> param, List<MultipartFile> files) {
        header = this.buildDefaultContentType(header, MediaType.MULTIPART_FORM_DATA_VALUE);
        final HttpHeaders headers = this.buildHeader(header);
        MultiValueMap<String, Object> body = this.buildMultipartBody(param, files);
        HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);
        return this.httpClient.exchange(url, HttpMethod.PUT, requestEntity, String.class).getBody();
    }

    /**
     * delete同步请求（参数拼接）.
     *
     * @param url    路径（必填）
     * @param header 请求头（可选）
     * @param param  参数（可选）
     * @return String
     */
    @Override
    public String httpDelete(String url, Map<String, String> header, Map<String, Object> param) {
        header = this.buildDefaultContentType(header, MediaType.APPLICATION_FORM_URLENCODED_VALUE);
        final HttpHeaders headers = this.buildHeader(header);
        final HttpEntity<String> requestEntity = new HttpEntity<>(headers);
        URI uri = getUri(url, this.buildMultiValueMap(param));
        return this.httpClient.exchange(uri, HttpMethod.DELETE, requestEntity, String.class).getBody();
    }

    /**
     * delete同步请求（json）.
     *
     * @param url    路径（必填）
     * @param header 请求头（可选）
     * @param json   参数（必填）
     * @return String
     */
    @Override
    public String httpDeleteByJson(String url, Map<String, String> header, String json) {
        header = this.buildDefaultContentType(header, MediaType.APPLICATION_JSON_VALUE);
        final HttpHeaders headers = this.buildHeader(header);
        final HttpEntity<String> requestEntity = new HttpEntity<>(json, headers);
        return this.httpClient.exchange(url, HttpMethod.DELETE, requestEntity, String.class).getBody();
    }

    /**
     * delete同步请求（FormBody表单）.
     *
     * @param url    路径（必填）
     * @param header 请求头（可选）
     * @param param  参数（可选）
     * @return String
     */
    @Override
    public String httpDeleteByForm(String url, Map<String, String> header, Map<String, Object> param) {
        header = this.buildDefaultContentType(header, MediaType.APPLICATION_FORM_URLENCODED_VALUE);
        final HttpHeaders headers = this.buildHeader(header);
        MultiValueMap<String, Object> body = this.buildFormBody(param);
        HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);
        return this.httpClient.exchange(url, HttpMethod.DELETE, requestEntity, String.class).getBody();
    }

    private URI getUri(String url, MultiValueMap<String, String> param) {
        UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(url);
        return builder.queryParams(param).build().encode().toUri();
    }

    private Map<String, String> buildDefaultContentType(Map<String, String> header, String contentType) {
        if (MapUtil.isEmpty(header)) {
            header = new HashMap<>(16);
        }
        if (CharSequenceUtil.isBlank(header.get(HttpHeaders.CONTENT_TYPE))) {
            header.put(HttpHeaders.CONTENT_TYPE, contentType);
        }
        return header;
    }

    private HttpHeaders buildHeader(Map<String, String> header) {
        HttpHeaders headers = new HttpHeaders();
        if (MapUtil.isNotEmpty(header)) {
            for (Map.Entry<String, String> entry : header.entrySet()) {
                headers.add(entry.getKey(), entry.getValue());
            }
        }
        return headers;
    }

    private MultiValueMap<String, String> buildMultiValueMap(Map<String, Object> param) {
        MultiValueMap<String, String> body = new LinkedMultiValueMap<>(16);
        if (MapUtil.isNotEmpty(param)) {
            for (Map.Entry<String, Object> entry : param.entrySet()) {
                final Object value = entry.getValue();
                body.add(entry.getKey(), Objects.nonNull(value) ? value.toString() : null);
            }
        }
        return body;
    }

    private MultiValueMap<String, Object> buildFormBody(Map<String, Object> param) {
        MultiValueMap<String, Object> body = new LinkedMultiValueMap<>(16);
        if (MapUtil.isNotEmpty(param)) {
            for (Map.Entry<String, Object> entry : param.entrySet()) {
                body.add(entry.getKey(), entry.getValue());
            }
        }
        return body;
    }

    private MultiValueMap<String, Object> buildMultipartBody(Map<String, Object> param, List<MultipartFile> multipartFiles) {
        MultiValueMap<String, Object> body = buildFormBody(param);
        if (CollUtil.isNotEmpty(multipartFiles)) {
            for (MultipartFile file : multipartFiles) {
                ByteArrayResource byteArrayResource = buildByteArrayResource(FilesUtil.getBytes(file), file.getSize(), file.getOriginalFilename());
                body.add(FileConstant.FILE, byteArrayResource);
            }
        }
        return body;
    }

    @NotNull
    private ByteArrayResource buildByteArrayResource(byte[] file, long size, String filename) {
        ByteArrayResource byteArrayResource = new ByteArrayResource(file) {
            @Override
            public long contentLength() {
                return size;
            }

            @Override
            public String getFilename() {
                return filename;
            }
        };
        return byteArrayResource;
    }
}
